package com.stack.knowyoubase.config;

import com.google.gson.Gson;
import com.stack.knowyoubean.bean.RepJson;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.stack.knowyoubase.constant.GlobalConst;
import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.io.PrintWriter;
import java.util.Collection;

/**
 * Security安全权限表控制配置
 * ingore是完全绕过了spring security的所有filter，相当于不走spring security
 * permitall没有绕过spring security，其中包含了登录的以及匿名的。
 *
 * @author stack
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    private Gson gson;

    @Resource
    private LoginConfigImpl loginConfig;

    /**
     * 解决登录用户限制时，不是手动调用注销方法时的session自动失效无法感知注销问题
     *
     * @return httpSessionEventPublisher
     */
    @Bean
    HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }

    /**
     * 加密配置
     *
     * @return 加密方式
     */
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    /**
     * 配置用户
     *
     * @param auth 权限信息
     * @throws Exception 异常
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(loginConfig);

    }


    /**
     * 忽略拦截的静态资源 不经过spring security的filter过滤器
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css/**", "/js/**", "/images/**")
                //忽略druid的拦截
                .antMatchers("/druid/**");
    }


    /**
     * 配置权限
     *
     * @param http Security
     * @throws Exception 异常
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //配置访问权限 顺序由高到低  访问路径,现在处于没有配权限状态
                .antMatchers(HttpMethod.GET, "/api/captcha/**").permitAll()
                .antMatchers(HttpMethod.POST, "/api/user/register").permitAll()
                .antMatchers(HttpMethod.POST, "/api/user/forgetPass").permitAll()
                .antMatchers(HttpMethod.PUT, "/api/course").hasRole("teacher")
                .antMatchers(HttpMethod.DELETE, "/api/course").hasRole("teacher")
                .antMatchers(HttpMethod.POST, "/api/course").hasRole("teacher")
                .antMatchers(HttpMethod.GET, "/api/homework").hasRole("teacher")
                .antMatchers(HttpMethod.POST, "/api/homework").hasRole("teacher")
                .antMatchers(HttpMethod.GET, "/api/homework/hid/**").hasRole("teacher")
                .antMatchers(HttpMethod.PUT, "/api/homework").hasRole("teacher")
                .antMatchers(HttpMethod.PUT, "/api/homework/batchNormal").hasRole("teacher")
                .antMatchers(HttpMethod.PUT, "/api/homework/batchStop").hasRole("teacher")
                .antMatchers(HttpMethod.DELETE, "/api/homework/batchDel").hasRole("teacher")
                .antMatchers(HttpMethod.GET, "/api/answer/info").hasRole("teacher")
                .antMatchers(HttpMethod.GET, "/api/answer/hid/**").hasRole("teacher")
                .antMatchers(HttpMethod.POST, "/api/knowledge").hasRole("teacher")
                .antMatchers(HttpMethod.PUT, "/api/knowledge").hasRole("teacher")
                .antMatchers(HttpMethod.DELETE, "/api/knowledge").hasRole("teacher")
                .antMatchers(HttpMethod.POST, "/api/videoUpload").hasRole("teacher")
                .antMatchers(HttpMethod.POST, "/api/courseware").hasRole("teacher")
                .antMatchers(HttpMethod.DELETE, "/api/courseware").hasRole("teacher")
                .antMatchers(HttpMethod.POST, "/api/labreport").hasRole("student")
                .antMatchers(HttpMethod.DELETE, "/api/labreport").hasRole("student")
                .antMatchers(HttpMethod.GET, "/api/examanalysis").hasRole("teacher")
                .antMatchers(HttpMethod.GET, "/api/prehomworkanalysis").hasRole("teacher")
                .antMatchers(HttpMethod.POST, "/api/atlasNode").hasRole("teacher")
                .antMatchers(HttpMethod.PUT, "/api/atlasNode").hasRole("teacher")
                .antMatchers(HttpMethod.DELETE, "/api/atlasNode").hasRole("teacher")
                .antMatchers(HttpMethod.POST, "/api/atlasNodeLink").hasRole("teacher")
                .antMatchers(HttpMethod.PUT, "/api/atlasNodeLink").hasRole("teacher")
                .antMatchers(HttpMethod.DELETE, "/api/atlasNodeLink").hasRole("teacher")
                .antMatchers(HttpMethod.GET, "/api/examanalysis").hasRole("teacher")
                .antMatchers(HttpMethod.GET, "/api/prehomworkanalysis").hasRole("teacher")
                .antMatchers(HttpMethod.GET, "/api/stu/score/analysis").hasRole("student")
                .antMatchers(HttpMethod.GET, "/api/teacher/score/analysis").hasRole("teacher")
                //任何请求都要授权
                .anyRequest().authenticated()
                .and()
                //登录相关配置
                .formLogin()
                //配置登录接口
                .loginProcessingUrl("/api/login")
                .usernameParameter("username")
                .passwordParameter("password")
                .successHandler((request, response, authentication) -> {
                    //登录成功后响应（跳转，response，返回json数据,authentication登录成功的用户信息）
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    //账户权限返回
                    Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
                    String role = null;
                    for (GrantedAuthority authority : authorities) {
                        role = authority.getAuthority();
                    }
                    //seession域中存入账号字段，用于校验是否存在入侵行为（注入）
                    HttpSession session = request.getSession();
                    //存入账户校验字段
                    session.setAttribute("userid", authentication.getName());
                    //定制响应信息
                    RepJson<String> repJson = new RepJson<>();
                    repJson.setCode(GlobalConst.SUCCESS_CODE);
                    repJson.setMsg("登录成功");
                    repJson.setData(role);
                    out.write(gson.toJson(repJson));
                    out.flush();
                    out.close();

                })
                .failureHandler((request, response, exception) -> {
                    //登录失败后响应（跳转，response，返回json数据,authentication登录成功的用户信息，安全性，用户名和密码错误不区分）
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    //定制响应信息 instanceof 比较类型是否相同
                    if (exception instanceof BadCredentialsException) {
                        RepJson<Object> repJson = new RepJson<>();
                        repJson.setCode(GlobalConst.ERROR_CODE);
                        repJson.setMsg("用户名或密码错误");
                        out.write(gson.toJson(repJson));
                    } else {
                        RepJson<Object> repJson = new RepJson<>();
                        repJson.setCode(GlobalConst.ERROR_CODE);
                        repJson.setMsg("用户已经登录");
                        out.write(gson.toJson(repJson));
                    }
                    out.flush();
                    out.close();

                })
                //permitALL（）代表不拦截与登录相关的请求与页面
                .permitAll()
                .and()
                //注销相关配置
                .logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/api/loginOut", "POST"))
                .logoutSuccessHandler((request, response, authentication) -> {
                    //登录成功后响应（跳转，response，返回json数据,authentication登录成功的用户信息）
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    RepJson<Object> repJson = new RepJson<>();
                    repJson.setCode(GlobalConst.SUCCESS_CODE);
                    repJson.setMsg("注销成功");
                    out.write(gson.toJson(repJson));
                    out.flush();
                    out.close();

                })
                .deleteCookies()
                .clearAuthentication(true)
                .invalidateHttpSession(true)
                //permitALL（）代表不拦截与注销相关的请求与页面
                .permitAll()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint((request, response, exception) -> {
                    //未认证后的响应 跳转登录（跳转，response，返回json数据,authentication登录成功的用户信息，安全性，用户名和密码错误不区分）
                    //未认证或请求权限不够响应403,强制跳转登录页
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    response.setStatus(403);
                    RepJson<Object> repJson = new RepJson<>();
                    repJson.setCode(GlobalConst.WARN_CODE);
                    repJson.setMsg("尚未登录");
                    out.write(gson.toJson(repJson));
                    out.flush();
                    out.close();
                })
                .and()
                .csrf().disable()
                .cors()
                .and()
                //配置最大会话数，禁止新用户登录 需要用户类实现equals方法和hashcode的方法用于比较用户是否相同
                .sessionManagement()
                .maximumSessions(1)
                //配置登录和普通使用不是同一个sessionID 防止固定会话攻击
                .and()
                .sessionFixation().migrateSession()

        ;
    }
}
